home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / timer / ds3100.md / timerMC.c < prev    next >
C/C++ Source or Header  |  1990-10-19  |  16KB  |  574 lines

  1. /*
  2.  * timerMC.c --
  3.  *
  4.  *    This file contains routines that manipulate the MC 146818 real-time
  5.  *    clock.
  6.  *
  7.  *    For a detailed explanation of the chip, see the "PMAX Desktop
  8.  *    Workstation Functional Specification, Revision 1.1" pages 62-66.
  9.  *
  10.  * Copyright (C) 1989 Digital Equipment Corporation.
  11.  * Permission to use, copy, modify, and distribute this software and
  12.  * its documentation for any purpose and without fee is hereby granted,
  13.  * provided that the above copyright notice appears in all copies.  
  14.  * Digital Equipment Corporation makes no representations about the
  15.  * suitability of this software for any purpose.  It is provided "as is"
  16.  * without express or implied warranty.
  17.  */
  18.  
  19. /*
  20.  * This chip has a real-time clock (RTC) and an interval timer.  The real-time
  21.  * clock is basically useless because it only has a resolution of seconds.
  22.  * The machine-independent part assumes that we have a free-running counter
  23.  * which has a resolution of milliseconds.  What we end up doing is
  24.  * faking the free running counter. The RTC generates an interrupt every
  25.  * second which we use to increment the seconds counter and clear the
  26.  * microseconds counter.  The interval timer is used to increment the
  27.  * microseconds counter.  We have a problem when interrupts are turned off
  28.  * since the counter doesn't get incremented.  This is fixed by comparing
  29.  * the difference in the hardware clock since the last interrupt against
  30.  * the difference in the software clock.  The hardware clock is supposed
  31.  * to go invalid 244 microseconds after the interrupt.  It isn't clear
  32.  * when the update-in-progress flag is set. My reading of the
  33.  * manual leads me to believe that it is set at the same time as the
  34.  * interrupt.  In this were true we could never read the hardware clock
  35.  * in the interrupt handler.  Either this isn't true, or we are usually
  36.  * delayed enough getting to the handler so that the clock is valid 
  37.  * again because the update-in-progress bit is never set (I ran a few
  38.  * tests).  
  39.  */
  40.  
  41. #ifndef lint
  42. static char rcsid[] = "$Header: /sprite/src/kernel/timer/ds3100.md/RCS/timerMC.c,v 9.5 90/10/19 15:58:06 rab Exp $ SPRITE (Berkeley)";
  43. #endif
  44.  
  45. #include <sprite.h>
  46. #include <sys.h>
  47. #include <timerInt.h>
  48. #include <timerTick.h>
  49. #include <spriteTime.h>
  50. #include <mach.h>
  51. #include <machMon.h>
  52. #include <prof.h>
  53. #include <timer.h>
  54. #include <machAddrs.h>
  55. #include <assert.h>
  56.  
  57. /*
  58.  * Control register A.
  59.  */
  60. #define REGA_UIP        0x80
  61. #define REGA_TIME_DIV        0x70
  62. #define REGA_RATE_SELECT    0x0F
  63.  
  64. /*
  65.  * Time base to use in the REGA_TIME_DIV field.
  66.  */
  67. #define REGA_TIME_BASE        0x20
  68.  
  69. /*
  70.  * Set the interval at 7.8125 ms.  RATE_US is the number of microseconds
  71.  * to add to the counter at each interrupt.  WHEN_TO_ADD_ONE says after
  72.  * how many intervals should one extra microsecond be added in.  This
  73.  * is necessary because the interval is actually 7812.5 microseconds.
  74.  */
  75. #define SELECTED_RATE        0x9
  76. #define RATE_US            7812
  77. #define WHEN_TO_ADD_ONE        0x1
  78.  
  79. /*
  80.  * Set up the interval structures. These are passed to Timer_CallBack
  81.  * and are used to compute the interval between callbacks. Since we have
  82.  * an interval of 7812.5 on the stupid ds3100 we have two structures and
  83.  * alternate passing them to Timer_CallBack.
  84.  * The interval is represented both as a Time, and as an integer. It is
  85.  * too expensive to convert between them later so set them both up here.
  86.  */
  87.  
  88.  
  89.  
  90. static Time lowTime = {0, 7812};
  91. static int lowInterval = 7812;
  92. static Time highTime = {0, 7813};
  93. static int highInterval = 7813;
  94.  
  95. /*
  96.  * Control register B.
  97.  */
  98. #define REGB_SET_TIME        0x80
  99. #define REGB_PER_INT_ENA    0x40
  100. #define REGB_UPDATE_INT_ENA    0x10
  101. #define REGB_DATA_MODE        0x04
  102. #define REGB_HOURS_FORMAT    0x02
  103.  
  104. /*
  105.  * Control register C.
  106.  */
  107. #define REGC_INT_PENDING    0x80
  108. #define REGC_PER_INT_PENDING    0x40
  109. #define REGC_UPDATE_INT_PENDING    0x10
  110.  
  111. /*
  112.  * Control register D.
  113.  */
  114.  
  115. #define REGD_VALID_TIME        0x80
  116. /*
  117.  * Pointers to registers. 
  118.  */
  119. volatile unsigned char    *secPtr  = (unsigned char *)(MACH_CLOCK_ADDR + 0x00);
  120. volatile unsigned char    *minPtr  = (unsigned char *)(MACH_CLOCK_ADDR + 0x08);
  121. volatile unsigned char    *hourPtr = (unsigned char *)(MACH_CLOCK_ADDR + 0x10);
  122. volatile unsigned char    *dayPtr  = (unsigned char *)(MACH_CLOCK_ADDR + 0x1C);
  123. volatile unsigned char    *monPtr  = (unsigned char *)(MACH_CLOCK_ADDR + 0x20);
  124. volatile unsigned char    *yearPtr = (unsigned char *)(MACH_CLOCK_ADDR + 0x24);
  125. volatile unsigned char    *regAPtr = (unsigned char *)(MACH_CLOCK_ADDR + 0x28);
  126. volatile unsigned char    *regBPtr = (unsigned char *)(MACH_CLOCK_ADDR + 0x2C);
  127. volatile unsigned char    *regCPtr = (unsigned char *)(MACH_CLOCK_ADDR + 0x30);
  128. volatile unsigned char    *regDPtr = (unsigned char *)(MACH_CLOCK_ADDR + 0x34);
  129.  
  130. /*
  131.  * We store a couple of things in the non-volatile ram.
  132.  */
  133.  
  134. #define NVR_ADDR (MACH_CLOCK_ADDR + 0x38)
  135.  
  136. volatile unsigned char *localOffsetNVMPtr = (unsigned char *) (NVR_ADDR + 0x00);
  137. volatile unsigned char *DSTAllowNVMPtr    = (unsigned char *) (NVR_ADDR + 0x04);
  138.  
  139. #define ONE_MILLION    1000000
  140.  
  141. /*
  142.  * The "free running counter"
  143.  */
  144. Timer_Ticks counter;
  145.  
  146. Boolean    callbackIntrsWanted = FALSE;
  147. Boolean profileIntrsWanted = FALSE;
  148.  
  149.  
  150. /*
  151.  * The RTC registers can only be accessed one byte at a time. This routine
  152.  * is used to write words into the non-volatile storage.
  153.  */
  154.  
  155. #define BYTECOPY(a,b,num) { \
  156.     int    i; \
  157.     for (i = 0; i < (num); i++) { \
  158.     ((char *) (b))[i] = ((char *) (a))[i]; \
  159.     } \
  160. }
  161.  
  162. /*
  163.  * Used for debugging. Counts number of times free-running counter was
  164.  * corrected.
  165.  */
  166. int     timerCorrectedClock;
  167.  
  168.  
  169. /*
  170.  *----------------------------------------------------------------------
  171.  *
  172.  * Timer_TimerInit --
  173.  *
  174.  *    Initialize the periodic timer.
  175.  *
  176.  *    N.B. This routine must be called before Timer_TimerStart.
  177.  *
  178.  * Results:
  179.  *    None.
  180.  *
  181.  * Side effects:
  182.  *    The timer is initialized.
  183.  *
  184.  *----------------------------------------------------------------------
  185.  */
  186. /*ARGSUSED*/
  187. void
  188. Timer_TimerInit(timer)
  189.     unsigned short     timer;
  190. {
  191.     *regAPtr = REGA_TIME_BASE | SELECTED_RATE;
  192.     *regBPtr = REGB_DATA_MODE | REGB_HOURS_FORMAT;
  193. }
  194.  
  195.  
  196. /*
  197.  *----------------------------------------------------------------------
  198.  *
  199.  * Timer_TimerStart --
  200.  *
  201.  *    Start the timer ticking.
  202.  *    ands starts the timer.
  203.  *
  204.  *    N.B. The timer must have been initialized with Timer_TimerInit
  205.  *    before this routine is called.
  206.  *
  207.  * Results:
  208.  *    None.
  209.  *
  210.  * Side effects:
  211.  *    The timer starts ticking.
  212.  *
  213.  *----------------------------------------------------------------------
  214.  */
  215. void
  216. Timer_TimerStart(timer)
  217.     register unsigned short timer;
  218. {
  219. #ifndef lint
  220.     unsigned char    dummy;
  221. #endif
  222.  
  223.     *regBPtr |= REGB_PER_INT_ENA;
  224. #ifndef lint
  225.     dummy = *regCPtr;
  226. #endif
  227.  
  228.     Mach_MonPrintf("Starting timer interrupts.\n");
  229.     if (timer == TIMER_CALLBACK_TIMER) {
  230.     callbackIntrsWanted = TRUE;
  231.     } else if (timer == TIMER_PROFILE_TIMER) {
  232.     profileIntrsWanted = TRUE;
  233.     } else {
  234.     panic("Timer_TimerStart: unknown timer %d\n", timer);
  235.     }
  236. }
  237.  
  238.  
  239. /*
  240.  *----------------------------------------------------------------------
  241.  *
  242.  * Timer_TimerInactivate --
  243.  *
  244.  *      Stops the specified timer such that it will cease counting and
  245.  *      also resests the mode register to 0.  If the timer has already
  246.  *      stopped and has set its output line high, clear the output so it
  247.  *      won't cause an interrupt (because we don't care that it has
  248.  *      expired).
  249.  *
  250.  * Results:
  251.  *    None.
  252.  *
  253.  * Side effects:
  254.  *    The timer is stopped.
  255.  *
  256.  *----------------------------------------------------------------------
  257.  */
  258. void
  259. Timer_TimerInactivate(timer)
  260.     register unsigned short timer;
  261. {
  262.     if (timer == TIMER_CALLBACK_TIMER) {
  263.     callbackIntrsWanted = FALSE;
  264.     } else if (timer == TIMER_PROFILE_TIMER) {
  265.     profileIntrsWanted = FALSE;
  266.     } else {
  267.     panic("Timer_TimerInactivate: unknown timer %d\n", timer);
  268.     }
  269.  
  270.     /*
  271.      * If neither type of timer interrupt is wanted, then disable
  272.      * timer interrupts.
  273.      */
  274.     if (!callbackIntrsWanted && !profileIntrsWanted) {
  275.     *regBPtr &= ~REGB_PER_INT_ENA;
  276.     }
  277. }
  278.  
  279.  
  280. /*
  281.  *----------------------------------------------------------------------
  282.  *
  283.  *  Timer_TimerServiceInterrupt --
  284.  *
  285.  *      This routine is called at every timer interrupt. 
  286.  *      It calls the timer callback queue handling if the callback timer 
  287.  *    expired and calls the profiling interrupt handling if the 
  288.  *    profile callback timer expired.
  289.  *
  290.  *  Results:
  291.  *    None.
  292.  *
  293.  *  Side Effects:
  294.  *    Routines on the timer queue may cause side effects. Profile
  295.  *    collect may take place. 
  296.  *    
  297.  *
  298.  *----------------------------------------------------------------------
  299.  */
  300. void
  301. Timer_TimerServiceInterrupt(statusReg, causeReg, pc)
  302.     unsigned int    statusReg;
  303.     unsigned int    causeReg;
  304.     Address         pc;
  305.     static unsigned    addOne = 0;
  306.     unsigned char     timerStatus;
  307.     Time        *timePtr;
  308.     unsigned int     interval;
  309.     Time_Parts        currentTODParts;
  310.     static int        previousSoftSeconds;
  311.     static int        previousHardSeconds;
  312.     int            softSeconds;
  313.     int            hardSeconds;
  314.     int            diff;
  315.     static Boolean    initialized = FALSE;
  316.  
  317.     static    int    eventDebug[1000];
  318.     static    int    dbgCtr = 0;
  319. #define INC(a) { (a) = ((a) + 1) % 1000; }
  320.  
  321.     timerStatus = *regCPtr;
  322.     eventDebug[dbgCtr] = 0;
  323.     if (timerStatus & REGC_PER_INT_PENDING) {
  324.     /*
  325.      * Increment the counter.
  326.      */
  327.     counter.microseconds += RATE_US;
  328.     if ((addOne & WHEN_TO_ADD_ONE) == WHEN_TO_ADD_ONE) {
  329.         counter.microseconds++;
  330.         timePtr = &highTime;
  331.         interval = highInterval;
  332.     } else {
  333.         timePtr = &lowTime;
  334.         interval = lowInterval;
  335.     }
  336.     addOne++;
  337.     if (counter.microseconds >= ONE_MILLION) {
  338.         /*
  339.          * We wrapped around. 
  340.          */
  341.         counter.microseconds = ONE_MILLION - 1;
  342.     }
  343.     }
  344.     if ((timerStatus & REGC_UPDATE_INT_PENDING)) {
  345.     /*
  346.      * RTC interrupt.
  347.      */
  348.     counter.seconds++;
  349.     counter.microseconds = 0;
  350.     eventDebug[dbgCtr] |= 0x1;
  351.     if ((*regAPtr & REGA_UIP) == 0) {
  352.         currentTODParts.seconds = *secPtr;
  353.         currentTODParts.minutes = *minPtr;
  354.         currentTODParts.hours = *hourPtr;
  355.         currentTODParts.dayOfMonth = *dayPtr;
  356.         currentTODParts.dayOfYear = -1;
  357.         currentTODParts.month = *monPtr-1;
  358.         currentTODParts.year = *yearPtr;
  359.         (void) Time_FromParts(¤tTODParts, FALSE, &hardSeconds);
  360.         softSeconds = counter.seconds;
  361.         diff = hardSeconds - previousHardSeconds;
  362.         eventDebug[dbgCtr] |= 0x4;
  363.         if (softSeconds - previousSoftSeconds != diff && initialized) {
  364.         /*
  365.          * Note that the software counter cannot be ahead of
  366.          * the hardware counter. We are only looking at the
  367.          * seconds, and the seconds are only incremented 
  368.          * during an interrupt. We only get one interrupt
  369.          * a second so we may be behind but not ahead.
  370.          */
  371.         if (softSeconds - previousSoftSeconds > diff) {
  372.             panic("Software time is ahead of hardware!\n");
  373.         }
  374.         counter.seconds = previousSoftSeconds + diff;
  375.         softSeconds = counter.seconds;
  376.         timerCorrectedClock++;
  377.         eventDebug[dbgCtr] |= 0x8;
  378.         }
  379.         previousHardSeconds = hardSeconds;
  380.         previousSoftSeconds = softSeconds;
  381.         initialized = TRUE;
  382.     }
  383.     }    
  384.     if (timerStatus & REGC_PER_INT_PENDING) {
  385.     if (mach_KernelMode) {
  386.         assert((statusReg & MACH_SR_KU_PREV) == 0);
  387.         /*
  388.          * Check for kernel profiling.  We'll sample the PC here.
  389.          */
  390.         if (profileIntrsWanted) {
  391.         TIMER_PROFILE_ROUTINE(pc);
  392.         } 
  393.     } else {
  394.         assert((statusReg & MACH_SR_KU_PREV) != 0);
  395.         Proc_GetCurrentProc()->Prof_PC = (int) pc;
  396.     }
  397.     TIMER_CALLBACK_ROUTINE(interval, *timePtr);
  398.     }
  399.     INC(dbgCtr);
  400. }
  401.  
  402.  
  403. /*
  404.  *----------------------------------------------------------------------
  405.  *
  406.  * Timer_CounterInit --
  407.  *
  408.  *    Initialize free-running counter.
  409.  *
  410.  * Results:
  411.  *    None.
  412.  *
  413.  * Side effects:
  414.  *    The specified counters begin to count.
  415.  *
  416.  *----------------------------------------------------------------------
  417.  */
  418. void
  419. Timer_CounterInit()
  420. {
  421.     counter.seconds = 0;
  422.     counter.microseconds = 0;
  423. }
  424.  
  425.  
  426. /*
  427.  *----------------------------------------------------------------------
  428.  *
  429.  *  Timer_GetCurrentTicks --
  430.  *
  431.  *      Return microseconds since boot.
  432.  *
  433.  *  Results:
  434.  *    The system up-time in ticks.
  435.  *
  436.  *  Side effects:
  437.  *    None.
  438.  *
  439.  *----------------------------------------------------------------------
  440.  */
  441. void
  442. Timer_GetCurrentTicks(ticksPtr)
  443.     Timer_Ticks    *ticksPtr;    /* Buffer to place current time. */
  444. {
  445.     DISABLE_INTR();
  446.  
  447.     *ticksPtr = counter;
  448.  
  449.     ENABLE_INTR();
  450. }
  451.  
  452.  
  453. /*
  454.  *----------------------------------------------------------------------
  455.  *
  456.  *  Timer_GetInfo --
  457.  *
  458.  *      Dummy routine to dump timer state.
  459.  *
  460.  *  Results:
  461.  *    None.
  462.  *
  463.  *  Side effects:
  464.  *    None.
  465.  *
  466.  *----------------------------------------------------------------------
  467.  */
  468. void
  469. Timer_TimerGetInfo()
  470. {
  471. }
  472.  
  473. /*
  474.  *----------------------------------------------------------------------
  475.  *
  476.  * TimerHardwareUniversalTimeInit --
  477.  *
  478.  *    Checks the battery backed up clock. If the contents are still
  479.  *    valid then we return them. 
  480.  *
  481.  *    IMPORTANT:  right now it looks like the ds3100 RTC is reset
  482.  *    by the prom during the boot.
  483.  *
  484.  * Results:
  485.  *    None.
  486.  *
  487.  * Side effects:
  488.  *    None.
  489.  *
  490.  *----------------------------------------------------------------------
  491.  */
  492.  
  493. void
  494. TimerHardwareUniversalTimeInit(timePtr, localOffsetPtr, DSTPtr)
  495.     Time *timePtr;        /* Buffer to hold universal time. */
  496.     int  *localOffsetPtr;    /* Buffer to hold local offset. */
  497.     Boolean *DSTPtr;        /* Buffer to hold DST allowed flag. */
  498. {
  499.     Time_Parts        timeParts;
  500.     int            seconds;
  501.     ReturnStatus    status;
  502.  
  503.     while ((*regAPtr & REGA_UIP) == 1) {
  504.     }
  505.     timeParts.seconds = *secPtr;
  506.     timeParts.minutes = *minPtr;
  507.     timeParts.hours = *hourPtr;
  508.     timeParts.dayOfMonth = *dayPtr;
  509.     timeParts.dayOfYear = -1;
  510.     timeParts.month = *monPtr-1;
  511.     timeParts.year = *yearPtr;
  512.     status = Time_FromParts(&timeParts, FALSE, &seconds);
  513.     if (status != SUCCESS) {
  514.     Mach_MonPrintf("Time stored in RTC is bogus.\n");
  515.     return;
  516.     }
  517.     if (*regDPtr & REGD_VALID_TIME == 0) {
  518.     Mach_MonPrintf(
  519.         "Warning: battery backed up TOD clock is invalid.\n");
  520.     return;
  521.     }
  522.     bzero((Address) timePtr, sizeof(Time));
  523.     timePtr->seconds = seconds;
  524.     BYTECOPY(localOffsetNVMPtr, localOffsetPtr, 4);
  525.     BYTECOPY(DSTAllowNVMPtr, DSTPtr, 4);
  526. }
  527.  
  528. /*
  529.  *----------------------------------------------------------------------
  530.  *
  531.  * TimerSetHardwareUniversalTime --
  532.  *
  533.  *    Sets the hardware RTC clock. Has to be called with interrupts
  534.  *    off.
  535.  *
  536.  *    IMPORTANT: The RTC is used to keep the free-running counter
  537.  *    up-to-date if we miss a few interrupts. DO NOT RESET THE RTC
  538.  *    WHILE THE KERNEL IS RUNNING!!! If you do system time will
  539.  *    not be linear and bad things will happen. Only call this routine
  540.  *    during startup and shutdown. As things now stand there is no
  541.  *    reason to call this routine at all since the RTC does not
  542.  *    appear to persist across reboots.
  543.  *
  544.  * Results:
  545.  *    None.
  546.  *
  547.  * Side effects:
  548.  *    The hardware RTC is set.
  549.  *
  550.  *----------------------------------------------------------------------
  551.  */
  552.  
  553. void
  554. TimerSetHardwareUniversalTime(timePtr, localOffset, DST)
  555.     Time *timePtr;        /* universal time. */
  556.     int  localOffset;        /* local offset. */
  557.     Boolean DST;        /* DST allowed flag. */
  558. {
  559.     Time_Parts        timeParts;
  560.  
  561.     Time_ToParts(timePtr->seconds, FALSE, &timeParts);
  562.     *regBPtr |= REGB_SET_TIME;
  563.     *secPtr = (unsigned char) timeParts.seconds;
  564.     *minPtr = (unsigned char) timeParts.minutes;
  565.     *hourPtr = (unsigned char) timeParts.hours;
  566.     *dayPtr = (unsigned char) timeParts.dayOfMonth;
  567.     *monPtr = (unsigned char) timeParts.month+1;
  568.     *yearPtr = (unsigned char) timeParts.year;
  569.     BYTECOPY(&localOffset,localOffsetNVMPtr,  4);
  570.     BYTECOPY(&DST, DSTAllowNVMPtr, 4);
  571.     *regBPtr &= ~REGB_SET_TIME;
  572. }
  573.